﻿/* 
 * Produzido por:
 *      Daniel Vargas
 *      Danilo Valvezon
 *      Fernanda Osaki
 *      Tiago Ricardo Almeida da Silva
 *      
 * FATEC São Caetano do Sul
 * Jogos NA6
 * */


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace bomberboyonline
{
    // Classe que representa personagem do jogo
    class Personagem : Microsoft.Xna.Framework.DrawableGameComponent
    {
        // Declaracoes de variaveis
        private const int CIMA = 0, DIREITA = 1, BAIXO = 2, ESQUERDA = 3, MORTE = 4; // Coostantes que representam direcao (imagem) do personagem
        private Texture2D textura;
        private int direcao, indexdir, direcaoAnterior, contMudImg; // variaveis de controle de imagens do personagem
        private int posicao, player; // posicao do personagem e numero de player dele
        private int passo; // controle de velocidade
        private int qtdRestanteBomba, poderFogo; // variaveis que representam itens de bombas e poder do fogo
        private bool morreu, detonador, chute; // vaiaveis de itens
        private int X, Y, W, H; // posicoes do jogador
        private int qtdVitorias; // quantidade de vitorias do personagem no game
        private Game game; 
        private SoundEffect somDrop, somMorte, somVitoria; // sons

        public Personagem(Game game, int player, int posicao)
            : base(game)
        {
            // Inicalizacao de variaveis
            this.game = game;
            this.player = player; // atribui numero de player
            
            this.posicao = posicao; // atribui posicao de quadrante
            X = Coordenadas.retangulos[posicao].X; // pega a coordenada x de quadrante
            Y = Coordenadas.retangulos[posicao].Y; // pega a coordenada y de quadrante
            W = 45; // largura de personagem
            H = 40; //altura de personagem
            
            // carrega arquivos externos. Imagens e sons
            textura = game.Content.Load<Texture2D>("imagens\\player" + player);
            somDrop = game.Content.Load<SoundEffect>("som\\drop");
            somMorte = game.Content.Load<SoundEffect>("som\\grito");
            somVitoria = game.Content.Load<SoundEffect>("som\\vitoria");

            // atribui valores iniciais de itens
            morreu = false;
            qtdRestanteBomba = 1;
            poderFogo = 1;
            passo = 2;
            detonador = false;
            chute = false;

            qtdVitorias = 0; // Ele nao possui nenhuma vitoria ainda

            // atribui valor de imagem e de variaveis de controle de imagens
            direcaoAnterior = BAIXO;
            direcao = BAIXO;
            indexdir = 4;            
            contMudImg = 0;           
        }

        public void inicializa(int posicao)
        {
            this.posicao = posicao; // atribui posicao de quadrante
            X = Coordenadas.retangulos[posicao].X; // pega a coordenada x de quadrante
            Y = Coordenadas.retangulos[posicao].Y; // pega a coordenada y de quadrante
            W = 45; // largura de personagem
            H = 40; //altura de personagem

            // atribui valores iniciais de itens
            morreu = false;
            qtdRestanteBomba = 1;
            poderFogo = 1;
            passo = 2;
            detonador = false;
            chute = false;

            // atribui valor de imagem e de variaveis de controle de imagens
            direcaoAnterior = BAIXO;
            direcao = BAIXO;
            indexdir = 4;
            contMudImg = 0;
            
            this.Visible = true;
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            verificaMorte(); // invoca metodo que verifica se personagem morreu e em caso afirmativo muda as imagens dele até sumir do jogo
            base.Update(gameTime);
        }

        /// <summary>
        /// Allows the game component to draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {
            // Pega o Spritebatch do Services
            SpriteBatch sBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
            // Desenha o personagem com sua resprectiva imagem de sprite. De acordo com indexdir e direcao pego as posicoes corretas da imagem para imprimir em tela
            sBatch.Draw(textura, new Rectangle(X, Y-40, W, 80),new Rectangle(W*indexdir,80*direcao,W,80), Color.White);

            base.Draw(gameTime);
        }

        // Metodo que verifica morte de player
        private void verificaMorte()
        {
            if (morreu) // se morreu
            {
                if (indexdir >= 4) // chegou a ultima imagem da morte
                {
                    game.Components.Remove(this); // removo player do jogo
                    //this.Visible = false;
                }
                else
                {
                    if (contMudImg == 10) // contador de mudanca de imagem chegou a valor desejado
                    {
                        contMudImg = 0; // zera contador
                        indexdir++; // incrementa posicao da imagem                        
                    }
                    else //se nao chegou
                    {
                        contMudImg++; // incrementa contador de mudanca de imagem
                    }                    
                }
            }
        }


        // metodo que recebe tecla digitada e faz o movimento respectivo
        public void movimentar(KeyboardState ks, ref List<int> posicoesOcupadas, ref List<int> posicoesItens)
        {
            bool cima = false, baixo = false, esquerda = false, direita = false;
            if (player == 1 && ks.IsKeyDown(Keys.Up)) cima = true; 
            if (player == 1 && ks.IsKeyDown(Keys.Down)) baixo = true;
            if (player == 1 && ks.IsKeyDown(Keys.Right)) direita = true;
            if (player == 1 && ks.IsKeyDown(Keys.Left)) esquerda = true;

            if (player == 2 && ks.IsKeyDown(Keys.W)) cima = true;
            if (player == 2 && ks.IsKeyDown(Keys.S)) baixo = true;
            if (player == 2 && ks.IsKeyDown(Keys.D)) direita = true;
            if (player == 2 && ks.IsKeyDown(Keys.A)) esquerda = true;

            if (player == 3 && ks.IsKeyDown(Keys.I)) cima = true;
            if (player == 3 && ks.IsKeyDown(Keys.K)) baixo = true;
            if (player == 3 && ks.IsKeyDown(Keys.L)) direita = true;
            if (player == 3 && ks.IsKeyDown(Keys.J)) esquerda = true;

            if (player == 4 && ks.IsKeyDown(Keys.G)) cima = true;
            if (player == 4 && ks.IsKeyDown(Keys.B)) baixo = true;
            if (player == 4 && ks.IsKeyDown(Keys.N)) direita = true;
            if (player == 4 && ks.IsKeyDown(Keys.V)) esquerda = true;

            if(cima)
            {
                 mudaImagemSprite(CIMA);
                 andarCima(ref posicoesOcupadas);
            }
            else if(baixo)
            {
                mudaImagemSprite(BAIXO);
                andarBaixo(ref posicoesOcupadas);
            }
            else if(esquerda)
            {
                mudaImagemSprite(ESQUERDA);
                andarEsquerda(ref posicoesOcupadas);
            }
            else if(direita)
            {
                mudaImagemSprite(DIREITA);
                andarDireita(ref posicoesOcupadas);
            }

            pegaItens(ref posicoesItens);

        }

        // Metodo que verifica se personagem entrou em posicao de item e atribui valor correspondente a item
        private void pegaItens(ref List<int> posicoesItens)
        {
            // Codigo para calcular item capturado
            if (posicoesItens.Contains(posicao))
            {
                IEnumerable<Item> itens = game.Components.OfType<Item>();
                foreach (Item item in itens)
                {
                    if (item.getPosicao() == posicao)
                    {
                        switch (item.getTipo())
                        {
                            case Item.BOMBAEXTRA:
                                setQtdRestanteBomba();                                
                                break;
                            case Item.CHUTE:
                                this.chute = true;
                                break;
                            case Item.DETONADOR:
                                this.detonador = true;
                                break;
                            case Item.VELOCIDADE:
                                if(this.passo < 6 ) this.passo++;
                                break;
                            case Item.PODERFOGO:
                                if(this.poderFogo < 8)this.poderFogo++;
                                break;
                            default:
                                break;
                        }
                        posicoesItens.Remove(posicao); // remove posicao de item
                        game.Components.Remove(item); // remove item do jogo
                        break;
                    }
                }
            }
        }

        // anda para direita caso nao colida com algum objeto
        private void andarDireita( ref List<int> posicoesOcupadas)
        {
            //incrementa os passos, movimentando personagem de lugar
            int XNovo = X + passo;
            if (colisao(ref posicoesOcupadas, XNovo, Y, posicao, DIREITA))
            {
                X = Coordenadas.retangulos[posicao].X+10;
            }
            else
            {
                X = XNovo;
                if (X+W >= Coordenadas.retangulos[posicao + 1].X + W / 2) posicao = posicao + 1;
            }

        }

        // anda para esquerda caso nao colida com algum objeto
        private void andarEsquerda(ref List<int> posicoesOcupadas)
        {
            //incrementa os passos, movimentando personagem de lugar
            int XNovo = X - passo;
            if (colisao(ref posicoesOcupadas, XNovo, Y, posicao, ESQUERDA))
            {
                X = Coordenadas.retangulos[posicao].X-10;
            }
            else
            {
                X = XNovo;
                if (X <= Coordenadas.retangulos[posicao - 1].X + W / 2) posicao = posicao - 1;
            }
        }

        // anda para baixo caso nao colida com algum objeto
        private void andarBaixo(ref List<int> posicoesOcupadas)
        {
            //incrementa os passos, movimentando personagem de lugar
            int YNovo = Y + passo;
            if (colisao(ref posicoesOcupadas, X, YNovo, posicao, BAIXO))
            {
                Y = Coordenadas.retangulos[posicao].Y+10;
            }
            else
            {
                Y = YNovo;
                if (Y+H >= Coordenadas.retangulos[posicao + 17].Y + H / 2) posicao = posicao + 17;
            }
        }

        // anda para cima caso nao colida com algum objeto
        private void andarCima(ref List<int> posicoesOcupadas)
        {
            //incrementa os passos, movimentando personagem de lugar
            int YNovo = Y - passo;
            if (colisao(ref posicoesOcupadas, X, YNovo, posicao, CIMA))
            {
                Y = Coordenadas.retangulos[posicao].Y-10;
            }
            else
            {
                Y = YNovo;
                if (Y <= Coordenadas.retangulos[posicao - 17].Y + H / 2) posicao = posicao - 17;
            }

        }


        // Metodo que verifica colisao, se estou andando para direita pego os quadrantes direito, direito acima, e direito abaixo
        // para compara se há alguma coisao com objetos. é necessario pegar os 3 quadarntes pois o personagem tem livre acesso
        // a quadrantes não definidos pela classe Coordenadas, ou seja, ele pode estar em 1, 2 ou até 4 quadrantes ao mesmo tempo
        private bool colisao(ref List<int> posicoesOcupadas, int x, int y,int posicao, int direcao)
        {            
            bool b1 = false, b2 = false, b3 = false, ret = false;
            Rectangle r = new Rectangle(x + 10, y + 10, W - 20, H - 20);
            
            int pos1=0, pos2=0, pos3=0;
            switch (direcao)
            {
                case CIMA:
                    pos1 = posicao - 17;
                    pos2 = posicao - 1 - 17;
                    pos3 = posicao + 1 - 17;
                    break;
                case BAIXO:
                    pos1 = posicao + 17;
                    pos2 = posicao - 1 + 17;
                    pos3 = posicao + 1 + 17;
                    break;
                case DIREITA:
                    pos1 = posicao + 1;
                    pos2 = posicao + 1 - 17;
                    pos3 = posicao + 1 + 17;
                    break;
                case ESQUERDA:
                    pos1 = posicao - 1;
                    pos2 = posicao - 1 - 17;
                    pos3 = posicao - 1 + 17;
                    break;
                default:
                    break;
            }
            r.Intersects(ref Coordenadas.retangulos[pos1], out b1);
            r.Intersects(ref Coordenadas.retangulos[pos2], out b2);
            r.Intersects(ref Coordenadas.retangulos[pos3], out b3);

            if (posicoesOcupadas.Contains(pos1) && b1)
            {
                ret = true;
            }
            else if (posicoesOcupadas.Contains(pos2) && b2)
            {
                ret = true;
            }
            if (posicoesOcupadas.Contains(pos3) && b3)
            {
                ret = true;
            }

            if (ret && chute) // Se personagem colidiu com algum objeto verifico se o mesmo é uma bomba e se ele tem a capacidade de chutar
                // caso sejam todas as condicoes afirmativas, adiciono direcao e passo para a bomba correspondente
            {
                IEnumerable<Bomba> bombas = game.Components.OfType<Bomba>();
                foreach (Bomba bomba in bombas)
                {
                    if (bomba.getPosicao() == pos1)
                    {
                        bomba.setMovimento(direcao, passo);
                        break;
                    }                    
                }
            }

            return ret;
        }

        // logica para mudar imagem de sprite
        private void mudaImagemSprite(int direcao)
        {            
            if (direcaoAnterior == direcao) // se direcao é a mesma que a anteriror muda imagem de passos 
            {
                // faco uma contagem de qts frames rodou para mudar a imagem, para nao mudar muito rapido
                if (contMudImg == 3)
                {
                    contMudImg = 0;
                    indexdir--; // decrementa posicao da imagem
                    if (indexdir < 0) indexdir = 3; // caso a imagem acabe, volta para imagem do primeiro passo.
                }
                else
                {
                    contMudImg++;
                }
            }
            else // se direcao é diferente da anterior muda direcao com imagem inicial
            {
                direcaoAnterior = this.direcao; // seta a direcao anterior para tal variavel
                this.direcao = direcao; // seta direcao para direcao que o jogador acabou de pressionar
                indexdir = 4; // seta a imagem inical da direcao
            }
        }

        // Mudanca de imagem de sprite quando personagem morre
        public void morrer()
        {
            direcaoAnterior = direcao;
            direcao = MORTE;
            indexdir = 0;
            morreu = true;
            somMorte.Play();
        }

        // Metodo que cria bomba na tela baseado na posicao do personagem. Decrementa qtdBombasRestantes de personagem
        public void lancaBomba(ref List<int> posicoesOcupadas, ref List<int> posicoesItens, ref List<int> posicoesFogo)
        {
            if (qtdRestanteBomba > 0)
            {
                if (!posicoesOcupadas.Contains(posicao))
                {
                    qtdRestanteBomba--;
                    somDrop.Play();

                    Bomba bomba = new Bomba(game, !detonador, player, posicao, ref posicoesOcupadas, ref posicoesItens, ref posicoesFogo);
                    game.Components.Add(bomba);
                    posicoesOcupadas.Add(posicao);
                }                
            }
        }

        // metodoq que detona bombas do personagem caso ele tenha a capacidade de detonador
        public void detonaBombas(ref List<int> posicoesOcupadas)
        {
            if (detonador)
            {
                IEnumerable<Bomba> bombas = game.Components.OfType<Bomba>();
                foreach (Bomba bomba in bombas)
                {
                    if (bomba.getPlayer() == player)
                    {
                        bomba.explode(ref posicoesOcupadas);
                        break;
                    }
                }
            }
        }        

        // Metodo que incrementa qtdRestanteBomba quando bomba explode
        public void setQtdRestanteBomba()
        {
            if(qtdRestanteBomba <9 )qtdRestanteBomba++;
            
        }      

        // gets e sets necessarios

        public int getPlayer()
        {
            return player;
        }

        public int getPosicao()
        {
            return posicao;
        }

        public bool getMorreu()
        {
            return morreu;
        }

        public int getPoderFogo()
        {
            return poderFogo;
        }


        // Metodo que incrementa vitorias do personagem
        public void setVitoria()
        {
            somVitoria.Play();
            qtdVitorias++;
        }

        public int getVitoria()
        {
            return qtdVitorias;
        }

        public void setPosicao(int posicao)
        {
            this.posicao = posicao;
        }
    }
}
